#!/usr/bin/python

#    This file is part of PTZ.
#
#    PTZ is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    PTZ is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with PTZ.  If not, see <http://www.gnu.org/licenses/>.
#
#    Copyright 2012  Jeremy Mortis (mortis@tansay.ca)

import Tkinter
import Tkconstants 
import tkMessageBox
import Image
import ImageTk
import time
import sys
import os
import logging
import ConfigParser
import re
import webbrowser
import serial
import collections
import glob

class GUI(Tkinter.Tk):

    def __init__(self):

        Tkinter.Tk.__init__(self)

        if sys.platform == 'win32':
            self.wm_iconbitmap("ptz.ico")

        self.protocol("WM_DELETE_WINDOW", self.delete_window)
        #self.resizable(width=False, height=False)

        if sys.platform == "win32":
            self.deviceList = ['COM1', 'COM2', 'COM3', 'COM4'] 
        else:
            self.deviceList = glob.glob('/dev/ttyACM*')
            self.deviceList.extend(glob.glob('/dev/ttyUSB*'))

        self.devicename = None
        self.device = None
        self.varDevice = Tkinter.StringVar()

        self.title("PTZ")
        self.show_menu()

        self.varPan = Tkinter.DoubleVar()
        self.varPan.trace("w", self.change_varPan)
        self.varTilt = Tkinter.DoubleVar()
        self.varTilt.trace("w", self.change_varTilt)

        logging.info("Getting configuration")
        self.get_config()

        self.topFrame = Tkinter.Frame(self, bd=1, relief=Tkconstants.RAISED)
        self.topFrame.pack(padx=10, pady=10)

        self.panLabel = Tkinter.Label(self.topFrame, text="Pan")
        self.panLabel.pack()

        self.panScale = Tkinter.Scale(self.topFrame, from_=-90, to=90, tickinterval=30, length=300, variable=self.varPan, orient=Tkconstants.HORIZONTAL, resolution=-1)
        self.panScale.pack()

        self.bottomFrame = Tkinter.Frame(self)
        self.bottomFrame.pack(fill=Tkconstants.X, pady=10)

        self.tiltFrame = Tkinter.Frame(self.bottomFrame, bd=1, relief=Tkconstants.RAISED, padx=20)
        self.tiltFrame.pack(side=Tkconstants.LEFT, padx=10)

        self.tiltLabel = Tkinter.Label(self.tiltFrame, text="Tilt")
        self.tiltLabel.pack()

        self.tiltScale = Tkinter.Scale(self.tiltFrame, from_=-30, to=30, tickinterval=15, variable=self.varTilt, resolution=-1)
        self.tiltScale.pack()

        self.zoomFrame = Tkinter.Frame(self.bottomFrame)
        self.zoomFrame.pack(side=Tkconstants.RIGHT, padx=10)

        self.zoominButton = Tkinter.Button(self.zoomFrame, text="+", command=self.pressed_zoominButton)
        self.zoominButton.pack(fill=Tkconstants.X)

        self.zoomLabel = Tkinter.Label(self.zoomFrame, text="Zoom")
        self.zoomLabel.pack()

        self.zoomoutButton = Tkinter.Button(self.zoomFrame, text="-", command=self.pressed_zoomoutButton)
        self.zoomoutButton.pack(fill=Tkconstants.X)

        self.statusBar = Tkinter.Label(self, text="hiho", bd=1, relief=Tkinter.SUNKEN, anchor=Tkinter.E)
        self.statusBar.pack(side=Tkinter.BOTTOM, fill=Tkinter.X)

        self.statusBar.configure(text="Not connected")

        self.varDevice.trace('w', self.change_varDevice)

        if self.varDevice.get() == "":
            tkMessageBox.showwarning("No device selected", "Please select a device using the device menu")
        else:
            self.change_varDevice()


        logging.info("GUI started")

    def get_config(self):

        self.configDefaults = {"devicename": "",
                               "pan": 0, 
                               "tilt": 0, 
                               "geometry": "324x263+50+50", 
                               }
        self.config = ConfigParser.RawConfigParser(self.configDefaults)

        if sys.platform == 'win32':
                self.configFilename = os.path.join(os.environ['APPDATA'], "ptz", "ptz.ini")
        else:
            self.configFilename = os.path.expanduser(os.path.join("~", ".ptz"))

        f = self.config.read(self.configFilename)

        self.geometry(self.config.get("DEFAULT", "geometry"))
        self.varDevice.set(self.config.get("DEFAULT", "devicename"))
        self.varPan.set(self.config.getfloat("DEFAULT", "pan"))
        self.varTilt.set(self.config.getfloat("DEFAULT", "tilt"))
            
    def show_menu(self, *args):

        self.menu = Tkinter.Menu(self)

        self.menuFile = Tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label="File", menu=self.menuFile, underline=0)
        self.menuFile.add_command(label="Quit", command=self.delete_window, underline=0) 

        self.menuDevice = Tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label="Device", menu=self.menuDevice, underline=0)
        if len(self.deviceList) == 0:
            self.menuDevice.add_radiobutton(label="No devices available", state=Tkconstants.DISABLED)
        else:
            for device in self.deviceList:
                self.menuDevice.add_radiobutton(label=device, variable=self.varDevice, value=device)

        self.menuHelp = Tkinter.Menu(self.menu, tearoff=0)
        self.menu.add_cascade(label="Help", menu=self.menuHelp, underline=0)
        self.menuHelp.add_command(label="Wiki", command=self.show_wiki, underline=0)
        self.menuHelp.add_command(label="About", command=self.show_infobox, underline=0)

        self.configure(menu=self.menu)

    def show_infobox(self):
        tkMessageBox.showinfo("", "PTZ 1.0\n\n" 
                              + "Copyright 2012 by Jeremy Mortis (mortis@tansay.ca)\n\n"
                              + "This program is free software made available under the GPLv3 license.")

    def show_wiki(self):
        webbrowser.open("https://code.google.com/p/ptz")

    def change_varDevice(self, *args):

        if self.device:
            self.device.close()
            self.device = None

        try:
            self.device = serial.Serial(self.varDevice.get(), "9600")
            self.device.setTimeout(1.0)
            self.statusBar.configure(text="Connected to %s" % self.varDevice.get())
            self.converse("")
        except Exception, e:
            print "Exception:", str(e)
            tkMessageBox.showerror("I/O Error on Open", "Unable to connect to remote device %s" % self.varDevice.get())
            self.device = None

    def change_varPan(self, *args):

        self.converse("p=%id" % int(self.varPan.get() + 90))

    def change_varTilt(self, *args):

        self.converse("t=%id" % int(self.varTilt.get() + 90))

    def pressed_zoominButton(self, *args):

        self.converse("z+1d")

    def pressed_zoomoutButton(self, *args):

        self.converse("z-1d")

    def delete_window(self):

        self.quit()
        self.save_config()

    def save_config(self):

        self.config.set("DEFAULT", "geometry", self.geometry())
        self.config.set("DEFAULT", "pan", self.varPan.get())
        self.config.set("DEFAULT", "tilt", self.varTilt.get())
        self.config.set("DEFAULT", "devicename", self.varDevice.get())

        with open(self.configFilename, 'wb') as configfile:
            self.config.write(configfile)        

    def converse(self, command):

        if not self.device:
            sys.stdout.write("No device connected\n")
            return

        self.device.write(command + "\n")

        line = ""
        while True:

            try:
                s = str(self.device.read())
            except Exception, e:
                print "Exception:", str(e)
                tkMessageBox.showerror("Error", "Device %s unreachable" % self.varDevice.get())

            if (s == ''):
                sys.stdout.write("Timeout waiting for prompt\n")
                break
            sys.stdout.write(s)
            if s == "\n":
                line = ""
            else:
                line += s
            if (line == "> ") and (self.device.inWaiting() == 0):
                break
            
if __name__ == '__main__':

    if sys.platform == 'win32':
        logFilename = os.path.join(os.environ['APPDATA'], "ptz", "ptz.log")
    else:
        logFilename = os.path.expanduser(os.path.join("~", "ptz.log"))

    logging.basicConfig(filename=logFilename, format='%(asctime)s %(message)s', level=logging.INFO)
    logging.info("Application started")

    gui = GUI()

    gui.mainloop()

    gui.destroy()

    logging.info("GUI terminated")


